﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;

namespace Useful.GUI
{
    public abstract class GroupingDataGridView : DataGridView
    {
        public class ToolTipTextNeededEventArgs : EventArgs
        {
            public string ToolTipText { get; set; }
            public int ColumnIndex { get; set; }
            public int RowIndex { get; set; }

            public ToolTipTextNeededEventArgs(int columnIndex, int rowIndex)
            {
                this.ColumnIndex = columnIndex;
                this.RowIndex = rowIndex;
            }
        }

        private int? firstRowIndex = null;
        private int? firstColIndex = null;
        private int? sortedColIndex = null;
        private SortOrder? sortOrder = null;
        private bool isSuspend = false;

        public bool IsSuspend
        {
            get
            {
                return isSuspend;
            }
            set
            {
                isSuspend = value;
            }
        }

        private readonly DataGridViewCellStyleContainer styleContainer = new DataGridViewCellStyleContainer();

        private readonly MethodInfo miActivateToolTip = null;

        public event EventHandler<DataGridViewCellEventArgs> ProcessCellDoubleClick;
        public event EventHandler<DataGridViewCellEventArgs> ProcessCellClick;
        public event EventHandler<DataGridViewCellFormattingEventArgs> ProcessCellFormatting;
        public event EventHandler<DataGridViewCellToolTipTextNeededEventArgs> ProcessCellToolTipTextNeeded;
        public event EventHandler<ToolTipTextNeededEventArgs> ProcessCellToolTipTextNeeded2 = delegate { };

        public bool SetCurrentCellByRightMouseClick { get; set; }
        public ContextMenuStrip ShowContextMenu { get; set; }
        public bool TwoDecimalSeparator { get; set; }


        public abstract void ClearGroupRow();
        public abstract void CleaAll();
        public abstract void GroupRows(string columnName);


        public GroupingDataGridView()
        {
            Assembly a = Assembly.GetAssembly(typeof(System.Windows.Forms.DataGridView /*CompiledOn*/));

            Type t = a.GetType("System.Windows.Forms.DataGridView");
            miActivateToolTip = t.GetMethod("ActivateToolTip", BindingFlags.NonPublic | BindingFlags.Instance);

            //SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true);
        }

        protected void CallActivateToolTip(string toolTipText, int ColumnIndex, int RowIndex)
        {
            miActivateToolTip.Invoke(this, new object[] { true, toolTipText, ColumnIndex, RowIndex });
        }

        public void InitDefault(bool readOnly, BindingSource bs, DataGridViewSelectionMode selectionMode)
        {
            InitDefault(readOnly, selectionMode);

            AllowUserToResizeRows = false;
            DataSource = bs;
        }

        public void InitDefault(bool readOnly, DataGridViewSelectionMode selectionMode)
        {
            AllowUserToAddRows = false;
            AllowUserToDeleteRows = false;
            ReadOnly = readOnly;
            MultiSelect = false;
            SelectionMode = selectionMode;
            AllowUserToOrderColumns = false;
            ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            AllowUserToResizeColumns = true;
            RowHeadersVisible = false;
            AutoGenerateColumns = false;
            SetCurrentCellByRightMouseClick = true;
        }

        public void InitDefault(bool readOnly, BindingSource bs, DataGridViewSelectionMode selectionMode, Control parent)
        {
            InitDefault(readOnly, bs, selectionMode);

            parent.Controls.Add(this);
            Dock = DockStyle.Fill;
        }

        public void SaveSortPos()
        {
            sortedColIndex = SortedColumn != null ? (int?)SortedColumn.Index : null;
            sortOrder = SortOrder;

            if (FirstDisplayedCell != null)
            {
                firstRowIndex = FirstDisplayedCell.RowIndex;
                firstColIndex = FirstDisplayedCell.ColumnIndex;
            }
        }

        public void RestoreSortPos()
        {
            if (sortedColIndex.HasValue && sortOrder.HasValue)
            {
                ListSortDirection? sortDir = null;

                if (sortOrder == SortOrder.Ascending)
                    sortDir = ListSortDirection.Ascending;
                else if (sortOrder == SortOrder.Descending)
                    sortDir = ListSortDirection.Descending;

                if (sortDir != null)
                    Sort(Columns[sortedColIndex.Value], sortDir.Value);
            }

            sortedColIndex = null;
            sortOrder = null;


            if (firstColIndex.HasValue && firstRowIndex.HasValue)
                FirstDisplayedCell = this[firstColIndex.Value, firstRowIndex.Value];

            firstRowIndex = null;
            firstColIndex = null;
        }

        public void SetFillCulumn(string name)
        {
            int with = 0;

            foreach (DataGridViewColumn c in Columns)
            {
                if (c.Visible && c.Name != name)
                    with += c.Width;
            }

            int clientWidth = Width - SystemInformation.VerticalScrollBarWidth - 2 * SystemInformation.Border3DSize.Width;

            Columns[name].Width = clientWidth - with;
        }

        public void SetFillCulumn(int index)
        {
            int with = 0;

            foreach (DataGridViewColumn c in Columns)
            {
                if (c.Visible && c.Index != index)
                    with += c.Width;
            }

            int clientWidth = Width - SystemInformation.VerticalScrollBarWidth - 2 * SystemInformation.Border3DSize.Width;

            Columns[index].Width = clientWidth - with;
        }

        public DataGridViewRow GetRow(int index)
        {
            try
            {
                return Rows[index];
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new ArgumentOutOfRangeException(String.Format("(1) CollapsedDataGridViewRowHeaderCell.GetRow: Не удалось получить строку по её индексу: {0}, всего строк: {1}, для грида: {2}", index, RowCount, Name), ex);
            }
            catch (Exception ex)
            {
                throw new ArgumentOutOfRangeException(String.Format("(2) CollapsedDataGridViewRowHeaderCell.GetRow: Не удалось получить строку по её индексу: {0}, всего строк: {1}, для грида: {2}", index, RowCount, Name), ex);
            }
        }

        public T GetDataRow<T>(int index)
        {
            DataGridViewRow row = GetRow(index);

            object obj = row.DataBoundItem;

            if (obj != null)
                return (T)obj;
            else
                throw new NullReferenceException("К строке не привязан объект DataBoundItem");
        }

        public T GetDataRow<T>(DataGridViewRow row)
        {
            return GetDataRow<T>(row.Index);
        }

        public DataGridViewColumn GetColumn(int index)
        {
            try
            {
                return Columns[index];
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new ArgumentOutOfRangeGuiException(String.Format("Не удалось получить столбец по его индексу: {0}, всего столбцов: {1}, для грида: {2}", index, ColumnCount, Name), ex);
            }
        }

        public void AddColumns(Type type)
        {
            List<PropertyInfo> _props = new List<PropertyInfo>(type.GetProperties(BindingFlags.Instance | BindingFlags.Public));

            List<PropertyInfo> props = _props.FindAll(pi =>
            {
                bool res = false;
                object[] attributes = pi.GetCustomAttributes(typeof(ExDisplayNameAttribute), false);

                if (attributes.Length == 1)
                    res = true;

                return res;
            });

            props.Sort((piX, piY) =>
            {
                object[] attributesX = piX.GetCustomAttributes(typeof(ExDisplayNameAttribute), false);
                object[] attributesY = piY.GetCustomAttributes(typeof(ExDisplayNameAttribute), false);

                if (attributesX.Length == 1 && attributesY.Length == 1)
                {
                    ExDisplayNameAttribute aX = (ExDisplayNameAttribute)attributesX[0];
                    ExDisplayNameAttribute aY = (ExDisplayNameAttribute)attributesY[0];

                    return Comparer<int>.Default.Compare(aX.Order, aY.Order);
                }
                else
                    throw new Exception("attributesX.Length != 1 || attributesY.Length != 1");
            });

            foreach (PropertyInfo pi in props)
            {
                object[] attributes = pi.GetCustomAttributes(typeof(ExDisplayNameAttribute), false);
                ExDisplayNameAttribute a = (ExDisplayNameAttribute)attributes[0];

                string toolTipText = null;

                object[] attributes2 = pi.GetCustomAttributes(typeof(ToolTipAttrubute), false);
                if (attributes2.Length != 0)
                {
                    ToolTipAttrubute tta = (ToolTipAttrubute)attributes2[0];
                    toolTipText = tta.ToolTipText;
                }

                string colName = a.ColumnName ?? pi.Name;

                if (pi.PropertyType == typeof(bool?))
                    this.AddNullBoolColumn(colName, a.DisplayName, pi.Name, a.AutoSizeColumn, toolTipText);
                else if (pi.PropertyType == typeof(bool))
                    this.AddBoolColumn(colName, a.DisplayName, pi.Name, a.AutoSizeColumn, toolTipText);
                else if (pi.PropertyType == typeof(DateTime))
                    this.AddCalendarTextColumn2(colName, a.DisplayName, pi.Name, DataGridViewAutoSizeColumnMode.AllCells, a.Format, toolTipText);
                else
                    this.AddTextColumn(colName, a.DisplayName, pi.Name, a.AutoSizeColumn, a.Format, toolTipText);

                this.Columns[colName].ReadOnly = a.ReadOnly;
            }
        }

        public void CreateColumns(Type type)
        {
            this.Columns.Clear();
            AddColumns(type);
        }

        public void SetAutoSizeColumnsMode(DataGridViewAutoSizeColumnMode dataGridViewAutoSizeColumnMode)
        {
            foreach (DataGridViewColumn c in Columns)
                c.AutoSizeMode = dataGridViewAutoSizeColumnMode;
        }

        public void SetNoneAutoSizeColumnsMode()
        {
            SetAutoSizeColumnsMode(DataGridViewAutoSizeColumnMode.None);
        }

        public Font GetFont(DataGridViewCellStyle style, FontStyle fontStyle)
        {
            return styleContainer.GetFont(style, fontStyle);
        }

        public void SetColumnsSortMode(DataGridViewColumnSortMode mode)
        {
            SetColumnsSortMode(this, mode);
        }

        public static void SetColumnsSortMode(DataGridView grid, DataGridViewColumnSortMode mode)
        {
            foreach (DataGridViewColumn c in grid.Columns)
                c.SortMode = mode;
        }

        public void SetColumnWidth(string name, int width)
        {
            Columns[name].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
            Columns[name].Width = width;
        }

        public void ResizeColumn(string name, DataGridViewAutoSizeColumnMode mode)
        {
            this.AutoResizeColumn(Columns[name].Index, mode);
        }

        public void ResizeAllColumns(DataGridViewAutoSizeColumnsMode mode)
        {
            this.AutoResizeColumns(mode);
        }

        //------------------------------------------------------------------//
        public static DataGridViewTextBoxColumn CreateTextColumn(string name, string header, string dataPropertyName)
        {
            DataGridViewTextBoxColumn res = new DataGridViewTextBoxColumn();

            AddColumn(null, res, name, header, dataPropertyName, null, null, null);

            return res;
        }

        public void AddColumns(DataGridViewAutoSizeColumnMode autoSizeColumnMode, DataGridViewColumnSortMode sortMode, object[][] listColumns)
        {
            for (int i = 0; i < listColumns.Length; i++)
            {
                if (listColumns[i].Length == 2)
                    AddTextColumn(listColumns[i][0].ToString(), listColumns[i][1].ToString(), autoSizeColumnMode).SortMode = sortMode;
                else if (listColumns[i].Length == 3)
                {
                    if (listColumns[i][2] is Type)
                    {
                        if (listColumns[i][2] == typeof(bool))
                            AddBoolColumn(listColumns[i][1].ToString(), listColumns[i][0].ToString(), listColumns[i][1].ToString(), autoSizeColumnMode, null).SortMode = sortMode;
                        else
                            throw new ArgumentException("Не поддерживаемый тип для AddTextColumns");
                    }
                    else if (listColumns[i][2] is DataGridViewAutoSizeColumnMode)
                    {
                        AddTextColumn(listColumns[i][0].ToString(), listColumns[i][1].ToString(), (DataGridViewAutoSizeColumnMode)(listColumns[i][2])).SortMode = sortMode;
                    }
                    else if (listColumns[i][2] is string)
                        AddTextColumn(listColumns[i][1].ToString(), listColumns[i][0].ToString(), listColumns[i][1].ToString(), autoSizeColumnMode, listColumns[i][2].ToString(), null).SortMode = sortMode;
                    else
                        throw new ArgumentException("Не поддерживаемый параметр для AddColumns");
                }
            }
        }

        public void AddColumns(object[][] listColumns)
        {
            AddColumns(DataGridViewAutoSizeColumnMode.AllCells, DataGridViewColumnSortMode.Automatic, listColumns);
        }

        public void AddColumns(DataGridViewAutoSizeColumnMode autoSizeColumnMode, object[][] listColumns)
        {
            AddColumns(autoSizeColumnMode, DataGridViewColumnSortMode.Automatic, listColumns);
        }

        public DataGridViewTextBoxColumn AddTextColumn(string header, string dataPropertyName)
        {
            return AddTextColumn(dataPropertyName, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null, null);
        }

        public DataGridViewTextBoxColumn AddTextColumn(string name, string header, string dataPropertyName, string format)
        {
            return AddTextColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, format, null);
        }

        public DataGridViewTextBoxColumn AddTextColumn(string name, string header, string dataPropertyName)
        {
            return AddTextColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null, null);
        }

        public DataGridViewTextBoxColumn AddTextColumn(string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode)
        {
            return AddTextColumn(dataPropertyName, header, dataPropertyName, sizeMode, null, null);
        }

        public DataGridViewTextBoxColumn AddTextColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode)
        {
            return AddTextColumn(name, header, dataPropertyName, sizeMode, null, null);
        }

        public DataGridViewTextBoxColumn AddTextColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string format, string toolTipText)
        {
            DataGridViewTextBoxColumn res = new DataGridViewTextBoxColumn();

            AddColumn(this, res, name, header, dataPropertyName, sizeMode, toolTipText, format);

            return res;
        }

        public DataGridViewCheckBoxColumn AddBoolColumn(string header, string dataPropertyName)
        {
            return AddBoolColumn(dataPropertyName, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null);
        }

        public DataGridViewCheckBoxColumn AddBoolColumn(string name, string header, string dataPropertyName)
        {
            return AddBoolColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null);
        }

        public DataGridViewCheckBoxColumn AddBoolColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string toolTiptext)
        {
            DataGridViewCheckBoxColumn res = new DataGridViewCheckBoxColumn();

            AddColumn(res, name, header, dataPropertyName, sizeMode, toolTiptext);

            res.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;

            return res;
        }

        private static void AddColumn(DataGridView grid, DataGridViewColumn column, string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode? sizeMode, string toolTipText, string format)
        {
            GuiHelper.DataGridViewAddColumn(grid, column, name, header, dataPropertyName, sizeMode, toolTipText, format);
        }

        private void AddColumn(DataGridViewColumn column, string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode? sizeMode, string toolTipText)
        {
            AddColumn(this, column, name, header, dataPropertyName, sizeMode, toolTipText, null);
        }

        public DataGridViewCheckBoxColumn AddNullBoolColumn(string name, string header, string dataPropertyName)
        {
            return AddNullBoolColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null);
        }

        public DataGridViewCheckBoxColumn AddNullBoolColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string toolTipText)
        {
            //DataGridViewNullCheckBoxColumn res = new DataGridViewNullCheckBoxColumn();
            DataGridViewCheckBoxColumn res = new DataGridViewCheckBoxColumn();

            AddColumn(res, name, header, dataPropertyName, sizeMode, toolTipText);

            res.ThreeState = true;

            return res;
        }

        public DataGridViewCalendarTextColumn AddCalendarTextColumn(string name, string header, string dataPropertyName)
        {
            return AddCalendarTextColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null);
        }

        public DataGridViewCalendarTextColumn AddCalendarTextColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string toolTipText)
        {
            DataGridViewCalendarTextColumn res = new DataGridViewCalendarTextColumn();

            AddColumn(this, res, name, header, dataPropertyName, sizeMode, toolTipText, null);

            return res;
        }

        public DataGridViewCalendarTextColumn AddCalendarTextColumn2(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string format, string toolTipText)
        {
            DataGridViewCalendarTextColumn res = new DataGridViewCalendarTextColumn();

            AddColumn(this, res, name, header, dataPropertyName, sizeMode, toolTipText, format);

            return res;
        }

        /*public void SetDoubleBuffered(bool value)
        {
            this.DoubleBuffered = value;
        }*/

        /*public DataGridViewComboBoxColumn AddComboBoxColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string format)
        {
            DataGridViewComboBoxColumn res = new DataGridViewComboBoxColumn();
            res.Name = name;
            res.HeaderText = header;
            res.DataPropertyName = dataPropertyName;
            res.SortMode = DataGridViewColumnSortMode.NotSortable;
            res.AutoSizeMode = sizeMode;

            if (format != null)
                res.DefaultCellStyle.Format = format;

            this.Columns.Add(res);

            return res;
        }*/

        public DataGridViewComboBoxColumn AddComboBoxColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string format, BindingSource bs, bool displayStyleForCurrentCellOnly, string displayMember, string valueMember)
        {
            DataGridViewComboBoxColumn res = new DataGridViewComboBoxColumn();

            AddColumn(this, res, name, header, dataPropertyName, sizeMode, null, format);

            res.DataSource = bs;
            res.DisplayStyleForCurrentCellOnly = displayStyleForCurrentCellOnly;
            res.DisplayMember = displayMember;
            res.ValueMember = valueMember;

            return res;
        }

        public DataGridViewComboBoxColumn AddComboBoxColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string format)
        {
            return AddComboBoxColumn(name, header, dataPropertyName, sizeMode, format, null, false, null, null);
        }

        public DataGridViewComboBoxColumn AddComboBoxColumn(string name, string header, string dataPropertyName)
        {
            return AddComboBoxColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null, null, false, null, null);
        }

        public DataGridViewComboBoxColumn AddComboBoxColumn(string name, string header, string dataPropertyName, BindingSource bs, string displayMember, string valueMember)
        {
            return AddComboBoxColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null, bs, true, displayMember, valueMember);
        }

        public DataGridViewButtonColumn AddButtonColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string format, bool useColumnTextForButtonValue)
        {
            DataGridViewButtonColumn res = new DataGridViewButtonColumn();

            AddColumn(this, res, name, header, dataPropertyName, sizeMode, null, format);

            res.Text = header;
            res.UseColumnTextForButtonValue = useColumnTextForButtonValue;

            return res;
        }

        public DataGridViewButtonColumn AddButtonColumn(string name, string header, string dataPropertyName)
        {
            return AddButtonColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null, true);
        }

        public DataGridViewImageColumn AddImageColumn(string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode)
        {
            DataGridViewImageColumn res = new DataGridViewImageColumn();

            AddColumn(this, res, name, header, dataPropertyName, sizeMode, null, null);

            return res;
        }

        public DataGridViewImageColumn AddImageColumn(string name, string header, string dataPropertyName)
        {
            return AddImageColumn(name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells);
        }
        //------------------------------------------------------------------//

        protected override void OnCellParsing(DataGridViewCellParsingEventArgs e)
        {
            if (TwoDecimalSeparator && (e.DesiredType == typeof(decimal?) || e.DesiredType == typeof(decimal)))
            {
                if (e.Value != null)
                {
                    string str = e.Value.ToString();

                    if (str != String.Empty)
                    {
                        decimal res;

                        if (Useful.Format.FormaterHelper.ConvertToDecimal(str, out res))
                        {
                            e.Value = res;
                            e.ParsingApplied = true;
                        }
                    }
                }
            }

            //if (!e.ParsingApplied)
            base.OnCellParsing(e);
        }

        public int GetSelectedRowCount()
        {
            int res = 0;

            List<int> rows = new List<int>();

            foreach (DataGridViewCell cell in SelectedCells)
            {
                if (!rows.Contains(cell.RowIndex))
                    res++;

            }

            return res;
        }

        protected override void OnCellMouseEnter(DataGridViewCellEventArgs e)
        {
            base.OnCellMouseEnter(e);

            if (e.ColumnIndex != -1 && e.RowIndex != -1)
            {
                int columnIndex = e.ColumnIndex;

                ToolTipTextNeededEventArgs ea = new ToolTipTextNeededEventArgs(columnIndex, e.RowIndex);

                CallProcessCellToolTipTextNeeded2(ea);

                if (!String.IsNullOrEmpty(ea.ToolTipText))
                {
                    CallActivateToolTip(ea.ToolTipText, columnIndex, e.RowIndex);
                }
            }
        }

        protected void CallProcessCellToolTipTextNeeded2(ToolTipTextNeededEventArgs e)
        {
            if(ProcessCellToolTipTextNeeded2 != null)
                ProcessCellToolTipTextNeeded2(this, e);
        }

        protected override void OnCellToolTipTextNeeded(DataGridViewCellToolTipTextNeededEventArgs e)
        {
            if (ProcessCellToolTipTextNeeded != null)
            {
                if (e.ColumnIndex != -1 && e.RowIndex != -1 && this.CurrentRow != null)
                    ProcessCellToolTipTextNeeded(this, e);
            }
            //else
            base.OnCellToolTipTextNeeded(e);
        }

        protected override void OnCellDoubleClick(DataGridViewCellEventArgs e)
        {
            if (ProcessCellDoubleClick != null)
            {
                if (e.ColumnIndex != -1 && e.RowIndex != -1 && this.CurrentRow != null)
                    CallProcessCellDoubleClick(e);
            }
            //else
            base.OnCellDoubleClick(e);
        }

        protected void CallProcessCellDoubleClick(DataGridViewCellEventArgs e)
        {
            if (ProcessCellDoubleClick != null)
                ProcessCellDoubleClick(this, e);
        }

        protected override void OnCellClick(DataGridViewCellEventArgs e)
        {
            if (ProcessCellClick != null)
            {
                if (e.ColumnIndex != -1 && e.RowIndex != -1 && this.CurrentRow != null)
                    ProcessCellClick(this, e);
            }

            base.OnCellClick(e);
        }

        protected override void OnCellMouseClick(DataGridViewCellMouseEventArgs e)
        {
            if ((ShowContextMenu != null) && e.Button == MouseButtons.Right && e.RowIndex != -1 && e.ColumnIndex != -1)
            {
                Rectangle rect = this.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
                ShowContextMenu.Show(this, rect.X + e.X, rect.Y + e.Y);
            }

            base.OnCellMouseClick(e);
        }

        protected override void OnCellFormatting(DataGridViewCellFormattingEventArgs e)
        {
            if (ProcessCellFormatting != null)
            {
                if (e.ColumnIndex != -1 && e.RowIndex != -1 && e.RowIndex < RowCount && e.ColumnIndex < ColumnCount)
                    ProcessCellFormatting(this, e);
            }
            else
                base.OnCellFormatting(e);
        }

        protected override void OnRowStateChanged(int rowIndex, DataGridViewRowStateChangedEventArgs e)
        {
            if ((e.StateChanged != DataGridViewElementStates.Visible && e.StateChanged != DataGridViewElementStates.Displayed) || !isSuspend)
                base.OnRowStateChanged(rowIndex, e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (SetCurrentCellByRightMouseClick && e.Button == MouseButtons.Right)
            {
                if (SelectedRows.Count <= 1)
                {
                    HitTestInfo hti = this.HitTest(e.X, e.Y);

                    if (hti.ColumnIndex >= 0 && hti.RowIndex >= 0)
                        this.CurrentCell = this[hti.ColumnIndex, hti.RowIndex];
                }
            }

            base.OnMouseDown(e);
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            HitTestInfo hti = base.HitTest(e.X, e.Y);

            if (hti.RowIndex == -1 && hti.ColumnIndex != -1 && Columns[hti.ColumnIndex].HeaderCell is DataGridViewColumnCheckedHeaderCell)
            {
                DataGridViewColumnCheckedHeaderCell ch = (DataGridViewColumnCheckedHeaderCell)Columns[hti.ColumnIndex].HeaderCell;

                if (ch.HitTest(new MouseEventArgs(e.Button, e.Clicks, e.X - hti.ColumnX, e.Y - hti.RowY, e.Delta)))
                    ch.ChangeValue();
                else
                    base.OnMouseClick(e);
            }
            else
                base.OnMouseClick(e);
        }
    }
}
